use crate::measurement::Measurement;
use anyhow::Result;
use clap::{Parser, ValueEnum};
use serde::{Deserialize, Deserializer};
use std::time::Instant;

#[derive(Parser, Debug)]
#[command(version, author, about = "压测工具")]
pub(crate) struct Cli {
    /// 指定配置文件，如果指定了配置文件，则其它参数将被忽略
    #[arg(short, long)]
    pub(crate) config: Option<String>,

    /// 接口路径
    pub(crate) url: Option<String>,

    /// 请求方法
    #[arg(value_enum, short, long, ignore_case =true, default_value_t = Method::Get)]
    pub(crate) method: Method,

    /// 请求头，如：-H Content-Type:application/json -H User-Agent:Mozilla/5.0
    #[arg(short = 'H', long = "header", action = clap::ArgAction::Append)]
    pub(crate) headers: Vec<String>,

    /// 表单参数，如：-f name=tom -f age=18
    #[arg(short, long, action = clap::ArgAction::Append)]
    pub(crate) form: Vec<String>,

    /// json参数，如：-j '{"name": "tom", "age": "18"}'
    #[arg(short, long, action = clap::ArgAction::Append)]
    pub(crate) json: Option<String>,

    /// 请求数
    #[arg(short = 'n', long = "num", default_value_t = 1)]
    pub(crate) requests: u64,

    /// 并发数
    #[arg(short, long = "thread", default_value_t = 1)]
    pub(crate) threads: u64,

    /// 将请求结果输出到文件
    #[arg(short, default_value = "output.txt")]
    pub(crate) output: String,
}

#[derive(Debug, Clone, ValueEnum)]
// #[serde(rename_all = "lowercase")] // 将小写字母反序列化
pub(crate) enum Method {
    Get,
    Post,
    Put,
    Delete,
}

impl Default for Method {
    fn default() -> Self {
        Method::Get
    }
}

impl<'de> Deserialize<'de> for Method {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match s.to_lowercase().as_str() {
            "get" => Ok(Method::Get),
            "post" => Ok(Method::Post),
            "delete" => Ok(Method::Delete),
            "put" => Ok(Method::Put),
            _ => Err(serde::de::Error::custom("不支持的请求方法"))
        }
    }
}

pub fn main() -> Result<()> {
    let cli = Cli::parse();
    let pm = Measurement::new(cli)?;
    let requests = pm.requests;
    let start = Instant::now();
    let error_num = pm.request()?;
    let duration = start.elapsed().as_secs_f64();

    println!("QPS：         {:.2}", (requests as f64) / duration);
    println!("平均耗时(s):  {:.2}", duration / (requests as f64));
    println!("总耗时(s)：   {:.2}", duration);
    println!(
        "错误率：      {:.2}%",
        (error_num / requests) as f64 * 100.0
    );

    Ok(())
}
